package org.activemonitoring.harvesters;

import java.util.Random;

import org.activemonitoring.domain.Measure;

public class MeasureBuilderImpl implements MeasureBuilder {

    private static final long serialVersionUID = 1L;

    protected static final ThreadLocal<String> correlationId = new ThreadLocal<String>();
    private static final Random random = new Random(System.currentTimeMillis());

    private Measure measure;

    private Harvester harvester;

    public MeasureBuilderImpl(Harvester harvester, String moduleName, String name) {
        super();
        this.harvester = harvester;
        this.measure = new Measure();
        this.measure.setModuleName(moduleName);
        this.measure.setName(name);
        this.measure.setStartTime(System.currentTimeMillis());
        this.measure.setCorrelationId(getCorrelationId());
    }

    protected String getCorrelationId() {
        String id = correlationId.get();
        if (id == null) {
            id = createCorrelationId();
            correlationId.set(id);
            // Assign zero to the id to determine the first measure for this thread
            this.measure.setId(0);
        } else {
            this.measure.setId(-1);
        }
        return id;
    }

    protected String createCorrelationId() {
        return this.measure.getModuleName() + "-" + random.nextLong() + "-" + this.measure.getStartTime();
    }

    @Override
    public MeasureBuilder error() {
        this.measure.setEndTime(System.currentTimeMillis());
        this.measure.setHasError(true);
        return this;
    }

    @Override
    public void stop() {
        if (this.measure.isHasError() == false) {
            // No error has occured so the end time has already been taken
            this.measure.setEndTime(System.currentTimeMillis());
        }
        // We should clear thread local when the top level measure has stop
        if (this.measure.getId() == 0) {
            correlationId.set(null);
        }
        this.harvester.harvest(this.measure);
    }

    public Measure getMeasure() {
        return this.measure;
    }
}
